package rov.data.loader;


import rov.ui.gfx.*;
import java.io.*;
import java.nio.*;
import java.util.*;
import java.util.regex.Pattern;
import javax.media.opengl.*;


// loads a VRML formatted 3D file
public class VRMLModelLoader implements IModelLoader {
    private static final String TOKEN_COMMENT = "#.+";
    private static final String TOKEN_SEPARATOR = "Separator";
    private static final String TOKEN_MATERIAL_BINDING = "MaterialBinding";
    private static final String TOKEN_MATERIAL = "Material";
    private static final String TOKEN_COORDINATE3 = "Coordinate3";
    private static final String TOKEN_INDEXED_FACE_SET = "IndexedFaceSet";

    private static final String TOKEN_POINTS = "point";
    private static final String TOKEN_COORD_INDEXES = "coordIndex";
    private static final String TOKEN_MATERIAL_INDEXES = "materialIndex";

    private static final String TOKEN_NODE_START = "{";
    private static final String TOKEN_NODE_END = "}";
    private static final String TOKEN_PRIM_START = "[";
    private static final String TOKEN_PRIM_END = "]";

    private GL2 mGl;
    private ArrayList<Float> mVerticies;
    private ArrayList<Integer> mIndicies;


    public VRMLModelLoader(GL2 gl) {
        mGl = gl;
    }


    public boolean checkModelFormat(InputStream model) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(model));

        return in.readLine().contains("VRML");
    }


    public Renderable loadModel(InputStream model) throws IOException {
        /*BufferedReader in = new BufferedReader(model);
        String input = in.readLine();

        while ((input = in.readLine()) != null) {
            int trunc = input.indexOf("#");

            if (trunc >= 0)
                input = input.substring(0, trunc);

            input = input.trim();
            Scanner tokens = new Scanner(input);

            while (tokens.hasNext()) {
                if (!processToken(tokens.next()))
                    return false;
            }
        }*/

        mVerticies = new ArrayList<Float>();
        mIndicies = new ArrayList<Integer>();

        Scanner tokens = new Scanner(model);
System.out.println("delim: " + tokens.delimiter());
//        tokens.useDelimiter("(" + tokens.delimiter().toString() + "|\\r\\n?|\\n)");
//System.out.println("delim: " + tokens.delimiter());

        while (tokens.hasNext()) {
            if (!processToken(tokens))
                return null;
        }

System.out.println("creating VBORenderable...");

        FloatBuffer verts = FloatBuffer.allocate(mVerticies.size());
        IntBuffer inds = IntBuffer.allocate(mIndicies.size());


//ByteBuffer ve = ByteBuffer.allocateDirect(mVerticies.size() * 4 /* sizeof(float) */);
//ByteBuffer in = ByteBuffer.allocateDirect(mIndicies.size() * 4 /* sizeof(int) */);
/*ve.order(ByteOrder.nativeOrder());
in.order(ByteOrder.nativeOrder());
FloatBuffer verts = ve.asFloatBuffer();
IntBuffer inds = in.asIntBuffer();*/


        for (int i = 0; i < mVerticies.size(); i++)
            verts.put(mVerticies.get(i) * 100);

        for (int i = 0; i < mIndicies.size(); i++)
            inds.put(mIndicies.get(i));

        VBORenderable modelData = new VBORenderable(mGl);
        modelData.init(2);
        modelData.setBufferContents(0, verts, mGl.GL_STATIC_DRAW);
        modelData.setBufferContents(1, inds, mGl.GL_STATIC_DRAW);
        return modelData;
    }


    private boolean processToken(Scanner tokens) {
///System.out.println("processToken");

        boolean ok = true;

        while (ok && tokens.hasNext()) {
            String tok = tokens.next();

            if (tok.matches(TOKEN_COMMENT)) {
                ok = processComment(tokens);
            } else if (tok.matches(TOKEN_SEPARATOR)) {
                ok = processSeparator(tokens);
            } else if (tok.matches(TOKEN_MATERIAL_BINDING)) {
                ok = processMaterialBinding(tokens);
            } else if (tok.matches(TOKEN_MATERIAL)) {
                ok = processMaterial(tokens);
            } else if (tok.matches(TOKEN_COORDINATE3)) {
                ok = processCoordinate3(tokens);
            } else if (tok.matches(TOKEN_INDEXED_FACE_SET)) {
                ok = processIndexedFaceSet(tokens);
            }
        }

        return ok;
    }


    private boolean processComment(Scanner tokens) {
///System.out.println("comment");

        Pattern delim = tokens.delimiter();
        tokens.useDelimiter("\n");

        if (tokens.hasNext())
            tokens.next();

        tokens.useDelimiter(delim);
        return true;
    }


    private boolean processSeparator(Scanner tokens) {
///System.out.println("separator");

        String tok = null;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_NODE_START)) {
            System.err.println("Error: VRML loader: after \"" + TOKEN_SEPARATOR + "\" must come a \"" + TOKEN_NODE_START + "\"");
            return false;
        }

        if (!processToken(tokens))
            return false;

///System.out.println("err? " + tokens.delimiter() + ", " + tokens.hasNext() + ", " + tokens.hasNextLine());

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_NODE_END)) {
///System.out.println("err: " + tok + ", \"" + tokens.nextLine() + "\", " + tokens.hasNextLine() + ", \"" + (tokens.hasNextLine() ? tokens.nextLine() : "nahh") + "\", " + tokens.hasNextByte());
            System.err.println("Error: VRML loader: \"" + TOKEN_SEPARATOR + "\" node was not completed by a \"" + TOKEN_NODE_END + "\"");
            return false;
        }

        return true;
    }


    private boolean processMaterialBinding(Scanner tokens) {
///System.out.println("material binding");

        boolean more = true;

        while (more) {
            if (!tokens.hasNext()) {
                System.err.println("Error: VRML loader: \"" + TOKEN_MATERIAL_BINDING + "\" node was not completed by a \"" + TOKEN_NODE_END + "\"");
                return false;
            }

            String tok = tokens.next();
            more = !tok.equals(TOKEN_NODE_END);
        }

        return true;
    }


    private boolean processMaterial(Scanner tokens) {
///System.out.println("material");

        boolean more = true;

        while (more) {
            if (!tokens.hasNext()) {
                System.err.println("Error: VRML loader: \"" + TOKEN_MATERIAL + "\" node was not completed by a \"" + TOKEN_NODE_END + "\"");
                return false;
            }

            String tok = tokens.next();
            more = !tok.equals(TOKEN_NODE_END);
        }

        return true;
    }


    private boolean processCoordinate3(Scanner tokens) {
///System.out.println("coordinate 3");

        String tok = null;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_NODE_START)) {
            System.err.println("Error: VRML loader: after \"" + TOKEN_COORDINATE3 + "\" must come a \"" + TOKEN_NODE_START + "\"");
            return false;
        }

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_POINTS)) {
            System.err.println("Error: VRML loader: expected \"" + TOKEN_POINTS + "\" inside of \"" + TOKEN_COORDINATE3 + "\" node");
            return false;
        }

        if (!processPoints(tokens))
            return false;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_NODE_END)) {
            System.err.println("Error: VRML loader: \"" + TOKEN_COORDINATE3 + "\" node was not completed by a \"" + TOKEN_NODE_END + "\"");
            return false;
        }

        return true;
    }


    private boolean processPoints(Scanner tokens) {
///System.out.println("points");

        String tok = null;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_PRIM_START)) {
            System.err.println("Error: VRML loader: after \"" + TOKEN_POINTS + "\" must come a \"" + TOKEN_PRIM_START + "\"");
            return false;
        }
/*
        String match = "";
        Pattern delim = tokens.delimiter();
        tokens.useDelimiter(",|" + TOKEN_PRIM_END);

        while (!match.equals(TOKEN_PRIM_END) && tokens.hasNext()) {
            tok = tokens.next();
            match = tokens.match().group();

///System.out.println("match: " + match);

            if (!processPoint(tok)) {
                tokens.useDelimiter(delim);
                return false;
            }
        }

        tokens.useDelimiter(delim);

        if (!match.equals(TOKEN_PRIM_END) && (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_PRIM_END))) {
            System.err.println("Error: VRML loader: \"" + TOKEN_POINTS + "\" node was not completed by a \"" + TOKEN_PRIM_END + "\"");
            return false;
        }
*/

        Pattern delim = tokens.delimiter();
        tokens.useDelimiter(TOKEN_PRIM_END);

        if (!tokens.hasNext() || (tok = tokens.next()) == null) {
            //tokens.useDelimiter(delim);
            System.err.println("Error: VRML loader: \"" + TOKEN_POINTS + "\" node was not completed by a \"" + TOKEN_PRIM_END + "\"");
            return false;
        }

        tokens.useDelimiter(delim);
        tokens.skip(TOKEN_PRIM_END);
        Scanner subTokens = new Scanner(tok);
        subTokens.useDelimiter(",");

        while (subTokens.hasNext()) {
            tok = subTokens.next();

            if (!processPoint(tok)) {
                return false;
            }
        }

        return true;
    }


    private boolean processPoint(String s) {
///System.out.println("point");

        String[] tokens = s.trim().split("\\s+");

///for (int i = 0; i < tokens.length; i++) System.out.println("p: " + tokens[i]);

        if (tokens.length != 3) {
            System.err.println("Error: VRML loader: all points in \"" + TOKEN_POINTS + "\" must be composed of 3 numbers: \"" + s + "\"");
            return false;
        }

        try {
            mVerticies.add(Float.parseFloat(tokens[0]));
        } catch (NumberFormatException e) {
            System.err.println("Error: VRML loader: x element in vertex is not valid: \"" + tokens[0] + "\"");
            return false;
        }

        try {
            mVerticies.add(Float.parseFloat(tokens[1]));
        } catch (NumberFormatException e) {
            System.err.println("Error: VRML loader: y element in vertex is not valid: \"" + tokens[1] + "\"");
            return false;
        }

        try {
            mVerticies.add(Float.parseFloat(tokens[2]));
        } catch (NumberFormatException e) {
            System.err.println("Error: VRML loader: z element in vertex is not valid: \"" + tokens[2] + "\"");
            return false;
        }

///System.out.println("(x,y,z) => (" + tokens[0] + ", " + tokens[1] + ", " + tokens[2] + ")");

        return true;
    }

/*
    private boolean processIndexedFaceSet(Scanner tokens) {
System.out.println("indexed face set");

        boolean more = true;

        while (more) {
            if (!tokens.hasNext()) {
                System.err.println("Error: VRML loader: \"" + TOKEN_INDEXED_FACE_SET + "\" node was not completed by a \"" + TOKEN_NODE_END + "\"");
                return false;
            }

            String tok = tokens.next();
            more = !tok.equals(TOKEN_NODE_END);

System.out.println("tok: " + tok);
        }

        return true;
    }
*/

    private boolean processIndexedFaceSet(Scanner tokens) {
///System.out.println("indexedFaceSet");

        String tok = null;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_NODE_START)) {
            System.err.println("Error: VRML loader: after \"" + TOKEN_INDEXED_FACE_SET + "\" must come a \"" + TOKEN_NODE_START + "\"");
            return false;
        }

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_COORD_INDEXES)) {
            System.err.println("Error: VRML loader: expected \"" + TOKEN_COORD_INDEXES + "\" inside of \"" + TOKEN_INDEXED_FACE_SET + "\" node");
            return false;
        }

///System.out.println("ifs1: " + tok);

        if (!processCoordIndexes(tokens))
            return false;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_MATERIAL_INDEXES)) {
            System.err.println("Error: VRML loader: expected \"" + TOKEN_COORD_INDEXES + "\" inside of \"" + TOKEN_INDEXED_FACE_SET + "\" node");
            return false;
        }

        if (!processMaterialIndexes(tokens))
            return false;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_NODE_END)) {
            System.err.println("Error: VRML loader: \"" + TOKEN_INDEXED_FACE_SET + "\" node was not completed by a \"" + TOKEN_NODE_END + "\"");
            return false;
        }

///System.out.println("ifs2: " + tok);

        return true;
    }


    private boolean processMaterialIndexes(Scanner tokens) {
///System.out.println("materialIndexes");

        String tok = null;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_PRIM_START)) {
            System.err.println("Error: VRML loader: after \"" + TOKEN_MATERIAL_INDEXES + "\" must come a \"" + TOKEN_PRIM_START + "\"");
            return false;
        }

        Pattern delim = tokens.delimiter();
        tokens.useDelimiter(TOKEN_PRIM_END);

        if (!tokens.hasNext() || (tok = tokens.next()) == null) {
            //tokens.useDelimiter(delim);
            System.err.println("Error: VRML loader: \"" + TOKEN_MATERIAL_INDEXES + "\" node was not completed by a \"" + TOKEN_PRIM_END + "\"");
            return false;
        }

        tokens.useDelimiter(delim);
        tokens.skip(TOKEN_PRIM_END);

        /*Scanner subTokens = new Scanner(tok);
        subTokens.useDelimiter("-1,*");

        while (subTokens.hasNext()) {
            tok = subTokens.next().trim();

            if (tok.length() > 0 && !processCoordIndex(tok)) {
                return false;
            }
        }*/

        return true;
    }


    private boolean processCoordIndexes(Scanner tokens) {
///System.out.println("coordIndexes");

        String tok = null;

        if (!tokens.hasNext() || (tok = tokens.next()) == null || !tok.equals(TOKEN_PRIM_START)) {
            System.err.println("Error: VRML loader: after \"" + TOKEN_COORD_INDEXES + "\" must come a \"" + TOKEN_PRIM_START + "\"");
            return false;
        }

        Pattern delim = tokens.delimiter();
        tokens.useDelimiter(TOKEN_PRIM_END);

        if (!tokens.hasNext() || (tok = tokens.next()) == null) {
            //tokens.useDelimiter(delim);
            System.err.println("Error: VRML loader: \"" + TOKEN_COORD_INDEXES + "\" node was not completed by a \"" + TOKEN_PRIM_END + "\"");
            return false;
        }

        tokens.useDelimiter(delim);
        tokens.skip(TOKEN_PRIM_END);
        Scanner subTokens = new Scanner(tok);
        subTokens.useDelimiter("-1,*");

        while (subTokens.hasNext()) {
            tok = subTokens.next().trim();

            if (tok.length() > 0 && !processCoordIndex(tok)) {
                return false;
            }
        }

        return true;
    }


    private boolean processCoordIndex(String s) {
///System.out.println("coordIndex: \"" + s + "\"");

        String[] tokens = s.split(",\\p{javaWhitespace}*");

///for (int i = 0; i < tokens.length; i++) System.out.println("p: " + tokens[i]);

        if (tokens.length != 3) {
            System.err.println("Error: VRML loader: all coord indexes in \"" + TOKEN_COORD_INDEXES + "\" must be composed of 4 numbers: \"" + s + "\"");
            return false;
        }

        try {
            mIndicies.add(Integer.parseInt(tokens[0]));
        } catch (NumberFormatException e) {
            System.err.println("Error: VRML loader: 1st element in triangle is not valid: \"" + tokens[0] + "\"");
            return false;
        }

        try {
            mIndicies.add(Integer.parseInt(tokens[1]));
        } catch (NumberFormatException e) {
            System.err.println("Error: VRML loader: 2nd element in triangle is not valid: \"" + tokens[1] + "\"");
            return false;
        }

        try {
            mIndicies.add(Integer.parseInt(tokens[2]));
        } catch (NumberFormatException e) {
            System.err.println("Error: VRML loader: 3rd element in triangle is not valid: \"" + tokens[2] + "\"");
            return false;
        }

///System.out.println("(x,y,z) => (" + tokens[0] + ", " + tokens[1] + ", " + tokens[2] + ")");

        return true;
    }
}

